/***********************************************
 Lines modified:
 7, 20, 32, 40, 86.
                             lu,xinlai  2008/5/8
***********************************************/
#ifndef _MODEL_H_
#define _MODEL_H_

/***********************************************/
//#include "surface.h" /*commented by lu,xinlai*/

#include "visKDTree.h"

#include <cassert>
#include <cmath>
/*-------lu,xinlai 2008/5/8----------------------
************************************************/

#include "grid.h"
#include "data.h"
#include <cfloat>

namespace visualization
{
class UnstructuredGrid;

class Model
{
	friend class UCDInputFile;

/*************************************/	
	//friend class Ray;
	friend class RayCastRenderer;
/*------------------------------------
                 lu,xinlai    2008/5/7
*************************************/

private:
	UnstructuredGrid grid;
	Data point_data;
	Data cell_data;

	double x_Interval, y_Interval, z_Interval;
	size_t x_PntNumber, y_PntNumber;

/*************************************************************/
	size_t pointNumber;
	std::vector<double> pointAtId;
	std::vector<double> dataValueAtId;

	kdTree *kdtree;

	/* The dimension of the points in the data field
	 * This variable is given perhaps without immediate
	 * use, but in future, the program may process two 
	 * dimensional data, and the variable is really set
	 * for that, when the variable dimension is determined
	 * when reading a data file, and may be used for 
	 * integration. 

	 * The variable may be used together with the function
	 * Model::getDataValueOfPoint(--) to garuntee that the 
	 * afterward processes can carry on properly.
	 
	 * The default is set to 0, since it's now not used.
	 * See function getDataDimension() in this file.
	*/
	int dimension;

	// The bounds of the data field
	double xMax, xMin, yMax, yMin, zMax, zMin;
	// The range of value of the data field
	double dataLow, dataHigh;
/*-------------------------------------------------------------
                  lu,xinlai  2008/5/7
**************************************************************/

public:
	Model()
	{
		// Since at most 3 items are needed.
		// Specularly, 1 is used only for scalar
		// field, and 2 and 3 are used for vector
		// field.
		pointAtId.reserve(3);
		dataValueAtId.reserve(3);
	}

	~Model()
	{
		delete kdtree;
	}
/*Begin*************************************************************************/
	/*
	*/
	//========================
	size_t getDataTupleSize()
	//========================
	{
		return point_data.getTupleSize();
	}

	/*
	*/
	//===========================
	size_t getPntNumberOfModel()
	//===========================
	{
		return pointNumber;
	}

	/*	
	*/
	//=====================
	int getDataDimension()
	//=====================
	{
		/*
		dimension = 3;
		return dimension;
		*/
		return point_data.getTupleSize();
	}

	bool getDataValueRange(double &low, double &high)
	{
		int dataAtColumn = 0;
		// Compute dataLow and dataHigh.
		// If the data is not 1 dimensional, abort!
		if(1 != getDataDimension())
		{
			cout << "Note that the file is about a vector field!, so you're asked to specify the data to \
			         compute the data range." << endl;
			cout << "What's the column of the data whose range you want to compute?" << endl;
			cin >> dataAtColumn;
		}

		dataLow = DBL_MAX;
		dataHigh = DBL_MIN;
		vector<double> data;
		for(unsigned int i = 1; i <= getPntNumberOfModel(); i++)
		{
			getDataValueOfPoint(i, data);
			if(data[dataAtColumn] < dataLow)
			{
				dataLow = data[dataAtColumn];
			}
			if(data[dataAtColumn] > dataHigh)
			{
				dataHigh = data[dataAtColumn];
			}
		}
		low = dataLow;
		high = dataHigh;

		return 1;
	}

	/* Here, the problem of data dimension has to be concerned.
     * In perticular, For the case of dimension being equal to 
	 * 2, the z coordinate of the returned data is set to 0.
	 * This is done through Model::dimension.
	 *
	 * Initially, the function is like this: std::vector<double> *getPointCoordinates(size_t pntId);
	 * where the returned value is a type of pointer!!! This may case some errors in some applicati-
	 * ons when the function is invoked more than one times. To avoid this, it is needed to put the
	 * returned value to other variable once the needed data is got.
	*/
	//===================================================================
	void getPointCoordinates(size_t pntId, std::vector<double> &coords);
	//===================================================================

	/* Given the id of a point, the method returns the data 
	 * value at that point.

	 * Addition:
	 * The dimensions of data grid and data value may not be
	 * consistent, for example, the case that there is a four
	 * dimensional data vector at a three dimensional point.
	 * This function handles with this like the way of 
	 * getPointCoordiantes(,) : it guarantees the two dimensions
	 * are consistent.So, the function is really a filter that 
	 * filters data that will not be used for flow visualizaiton.
	 * The funciton may be used together with the variable 
	 * "Model::dimension".
	 *
	 * The initial function is like this :
	 * std::vector<double> *getDataValueOfPoint(size_t pntId);
	 *
	 * Refer to getPointCoordinates(....) for the reasons.
	*/
	//================================================================
	void getDataValueOfPoint(size_t pntId, std::vector<double> &val);
	//================================================================
	
	/*
	*/
	//==================
	void buildKDTree();
	//==================

	/* Description: interpolation method.
	 
	 * Function:
	 * The function receives a double pointer which indecates the 
	 * position to be interpolated, and gives interpolation results 
	 * by simply pushing them back into a vector if the position
	 * is near enough to a set of points in the data field, that
	 * is, it has "neightbors".

	 * Addition:
	 * The dimension of the data may be larger than that of position,
	 * for emxaple, a three dimensional points containing a data vector
	 * with dimension more than 3
	 * In this case, the funciton simply intepolate without concerning 
	 * the dimension problem.

	 * parameters:	 
	 * @pos       : a pointer of type double to the position to be interpolated
	 * @nDim      : the dimensions of pos
	 * @dataValue : to save the interpolation results to it's back, "without 
	                clearing the vector"
	 * @nearNum   : the number of the nearest neighbours used for kdtree searching
	                with default 10
	 * @disError  : distance error used for searching with default 1e-8
	 * @return    : indicate whether interpolation is successfull.
	*/
	//=====================================================
	bool interpolatedValue(const double *pos,                    
						   const int nDim,                       
						   vector<double> *dataValue, 
						   const int nearNum = 10,               
						   const double distError = 1e-8         
						   );
	//=====================================================

	void getBound(double &xmin, double &xmax, 
				  double &ymin, double &ymax,
				  double &zmin, double &zmax)
	{
		xmin = xMin;
		xmax = xMax;

		ymin = yMin;
		ymax = yMax;

		zmin = zMin;
		zmax = zMax;
	}

	double getXMin()
	{ return xMin; }
	double getXMax()
	{ return xMax; }
	double getYMin()
	{ return yMin; }
	double getYMax()
	{ return yMax; }
	double getZMin()
	{ return zMin; }
	double getZMax()
	{ return zMax; }
/*-------------------------------------------------------------------------------
				                    lu,xinlai  008/5/7
End*****************************************************************************/

	void BoundBox(double *left, double *top, double *right,
		double *bottom, double *n, double *f) const
	{
		grid.BoundBox(left, top, right, bottom, n, f);
	}

	//void ShadeSurface(const Shader &shader)
	//{
	//	//grid.ShadeSurface(shader);
	//}

	//void DrawSurface(bool face_color = false)
	//{
	//	grid.DrawSurface(face_color);
	//}

	//void DrawSurfaceWithLight()
	//{
	//	grid.DrawSurfaceWithLight();
	//}

	//void MarkSurfaceIDs()
	//{
	//	grid.MarkSurfaceIDs();
	//}

	//CellAccess *CreateCellAccess(size_t i)
	//{
	//	//return grid.CreateCellAccess(i);
	//}
};

}

#endif

